package com.monkey.service;

import com.monkey.core.ErrorCode;
import com.monkey.core.WebRet;
import com.monkey.generator.dao.TMarryOrderMapper;
import com.monkey.generator.dao.TMonkeyMapper;
import com.monkey.generator.dao.TMonkeyReproductMapper;
import com.monkey.generator.model.*;
import com.monkey.model.comparator.*;
import com.monkey.model.monkey.*;
import com.monkey.model.user.User;
import com.monkey.util.CountUtil;
import com.monkey.util.DateUtil;
import com.monkey.util.HelpUtil;
import com.monkey.util.WebConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.monkey.service.MonkeyService.*;
import static com.monkey.service.UserService.*;

/**
 * desc:
 * auth: chenxiaobin
 * data: 2018/1/4.
 */
@Service
@Transactional
public class MarryService {
    private static Logger logger = LoggerFactory.getLogger(MarryService.class);
    private static final String REDIS_PRE = "marry.";
    private List<MonkeyTradeData> sellList = new ArrayList<>();

    @Resource
    private UserService userService = null;

    @Resource
    private MonkeyService monkeyService = null;

    @Resource
    private TMonkeyMapper monkeyMapper = null;

    @Resource
    private TMonkeyReproductMapper monkeyReproductMapper = null;

    @Resource
    private TMarryOrderMapper marryOrderMapper = null;

    @Resource
    private HelpUtil limitUtil = null;

    @Resource
    private CountUtil countUtil = null;

    @Resource
    private DateUtil dateUtil = null;

    @Resource
    private WebConfig webConfig = null;

    @Resource
    private RedisTemplate<String, MonkeyTradeData> template = null;

    public void init() {
        // 还原redis数据
        ValueOperations<String, MonkeyTradeData> ops = template.opsForValue();
        Set<String> keys = template.keys(REDIS_PRE + "*");
        logger.info("marry from redis:{}", keys);
        for (String key : keys) {
            logger.info("marry load monkey:{}", key);
            MonkeyTradeData data = ops.get(key);
            User user = userService.getUser(data.getUserId());
            if (null == user) {
                logger.info("marry load user error:{}", data.getUserId());
                continue;
            }
            Monkey monkey = monkeyService.getMonkey(data.getMonkeyId());
            if (null == monkey) {
                logger.info("marry load monkey error:{}", data.getMonkeyId());
                continue;
            }
            data.setElement(monkey.getElement());
            monkey.setStatus(MONKEY_STATUS_MARRY);
            sellList.add(data);
        }
        logger.info("marry from redis success");
    }

    public float getPrice(int monkeyId) {
        synchronized (this) {
            for (MonkeyTradeData d : sellList) {
                if (d.getMonkeyId() == monkeyId) {
                    return d.getPrice();
                }
            }
        }
        return 0;
    }

    public WebRet sell(User user, int monkeyId, float price) {
        int userId = user.getId();

        // 是否绑定钱包
        if (user.getWallet().isEmpty()) {
            return new WebRet(ErrorCode.WALLET_NO_BIND);
        }

        // 是否激活账号
        if (!user.getEnable()) {
            return new WebRet(ErrorCode.ACCOUNT_DISABLE);
        }

        int weight = webConfig.getInt("marry.sell.weight");
        Monkey monkey = monkeyService.getMonkey(monkeyId);
        if (userId != monkey.getUserId()) {
            return new WebRet(ErrorCode.NO_OWNER_MONKEY);
        }
        if (monkey.getStatus() != MONKEY_STATUS_NORMAL) {
            return new WebRet(ErrorCode.MONKEY_HAS_ONLY);
        }
        if (monkey.getWeight() < weight) {
            return new WebRet(ErrorCode.MONKEY_LESS_WEIGHT);
        }

        // 生育时间
        if (monkey.getReproductCD().after(new Date())) {
            return new WebRet(ErrorCode.REPRODUCT_CD_ERROR);
        }

        // 鹊桥挂单手续费
        float water = webConfig.getFloat("marry.sell.water");
        if (user.getMoney().floatValue() < water) {
            return new WebRet(ErrorCode.WALLET_LESS_ERROR);
        }

        MonkeyTradeData data = new MonkeyTradeData(monkey, userId, price);
        synchronized (this) {
            for (MonkeyTradeData d : sellList) {
                if (d.getMonkeyId() == monkeyId) {
                    return new WebRet(ErrorCode.ORDER_HAS_EXIST);
                }
            }
            sellList.add(data);
        }

        // 扣除挂单手续费
        if (ErrorCode.SUCCESS != userService.updateUserMoney(user, monkeyId, -water, water, MONKEY_STATUS_NORMAL, ACTION_MARRY_SELL)) {
            return new WebRet(ErrorCode.WRITE_DB_ERROR);
        }

        // 写库
        TMarryOrder order = new TMarryOrder();
        order.setUserid(userId);
        order.setMonkeyid(monkeyId);
        order.setMoney(new BigDecimal(price));
        order.setStatus(ORDER_CREATE);
        order.setCreatetime(new Date());
        if (0 == marryOrderMapper.insert(order)) {
            logger.info("marry order insert db error:{}, {}", userId, monkeyId);
        }

        // 标注状态
        monkey.setStatus(MONKEY_STATUS_MARRY);
        ValueOperations<String, MonkeyTradeData> ops = template.opsForValue();
        ops.set(REDIS_PRE + monkeyId, data);
        return new WebRet(ErrorCode.SUCCESS);
    }

    public WebRet cancel(int userId, int monkeyId) {
        Monkey monkey = monkeyService.getMonkey(monkeyId);
        if (userId != monkey.getUserId()) {
            return new WebRet(ErrorCode.NO_OWNER_MONKEY);
        }
        if (monkey.getStatus() != MONKEY_STATUS_MARRY) {
            return new WebRet(ErrorCode.MONKEY_STATUS_ERROR);
        }

        // 重置状态
        monkey.setStatus(MONKEY_STATUS_NORMAL);
        synchronized (this) {
            Iterator<MonkeyTradeData> iter = sellList.iterator();
            while (iter.hasNext()) {
                MonkeyTradeData data = iter.next();
                if (data.getMonkeyId() == monkeyId) {
                    iter.remove();
                    break;
                }
            }
        }

        // 更新记录
        TMarryOrder order = new TMarryOrder();
        order.setStatus(ORDER_CANCEL);
        TMarryOrderExample example = new TMarryOrderExample();
        example.or().andUseridEqualTo(userId).andMonkeyidEqualTo(monkeyId).andStatusEqualTo(ORDER_CREATE);
        example.setOrderByClause("createTime DESC");
        example.setLimit(1);
        if (0 == marryOrderMapper.updateByExampleSelective(order, example)) {
            logger.info("marry order update cancel db error:{}, {}", userId, monkeyId);
        }

        template.delete(REDIS_PRE + monkeyId);
        return new WebRet(ErrorCode.SUCCESS);
    }

    public List<MonkeyTradeData> getList(int monkeyId, MonkeyTradeSort sort) {
        List<MonkeyTradeData> ret = new ArrayList<>();
        synchronized (this) {
            ret.addAll(sellList);
        }

        // 指定宠物
        if (0 != monkeyId) {
            List<MonkeyTradeData> tmp = new ArrayList<>();
            for (MonkeyTradeData data : ret) {
                if (data.getMonkeyId() == monkeyId) {
                    tmp.add(data);
                    return tmp;
                }
            }
            return new ArrayList<>();
        }

        int specify = sort.getSpecify();
        // 指定代数
        if (-1 != specify) {
            List<MonkeyTradeData> tmp = new ArrayList<>();
            for (MonkeyTradeData data : ret) {
                if (data.getGeneration() == specify) {
                    tmp.add(data);
                }
            }

            tmp.sort(new MonkeyTradePriceComparator(true));
            if (!limitUtil.getListWithMonkeyTradeData(tmp, sort.getOffset(), sort.getNum())) {
                return new ArrayList<>();
            }
            return tmp;
        }

        // 代数排序
        if (sort.isGeneration()) {
            ret.sort(new MonkeyTradeGenerationComparator(sort.getGeneration()));
            if (!limitUtil.getListWithMonkeyTradeData(ret, sort.getOffset(), sort.getNum())) {
                return new ArrayList<>();
            }
            return ret;
        }

        // 体重排序
        if (sort.isWeight()) {
            ret.sort(new MonkeyTradeWeightComparator(sort.getWeight()));
            if (!limitUtil.getListWithMonkeyTradeData(ret, sort.getOffset(), sort.getNum())) {
                return new ArrayList<>();
            }
            return ret;
        }

        // 生育排序
        if (sort.isReproduct()) {
            ret.sort(new MonkeyTradeReproductComparator(sort.getReproduct()));
            if (!limitUtil.getListWithMonkeyTradeData(ret, sort.getOffset(), sort.getNum())) {
                return new ArrayList<>();
            }
            return ret;
        }

        // 价格排序
        if (sort.isPrice()) {
            ret.sort(new MonkeyTradePriceComparator(sort.getPrice()));
            if (!limitUtil.getListWithMonkeyTradeData(ret, sort.getOffset(), sort.getNum())) {
                return new ArrayList<>();
            }
            return ret;
        }

        // 默认排序
        ret.sort(new MonkeyTradePriceComparator(true));
        if (!limitUtil.getListWithMonkeyTradeData(ret, sort.getOffset(), sort.getNum())) {
            return new ArrayList<>();
        }
        return ret;
    }

    public MonkeyMarryData marry(User user, int monkeyId, int myMonkeyId, Float price) {
        int userId = user.getId();

        // 是否绑定钱包
        if (user.getWallet().isEmpty()) {
            return new MonkeyMarryData(ErrorCode.WALLET_NO_BIND);
        }

        // 是否激活账号
        if (!user.getEnable()) {
            return new MonkeyMarryData(ErrorCode.ACCOUNT_DISABLE);
        }

        if (price <= 0) {
            return new MonkeyMarryData(ErrorCode.ORDER_LESS_ZERO);
        }

        // 判断余额是否足够
        MonkeyTradeData data = null;
        synchronized (this) {
            Iterator<MonkeyTradeData> iter = sellList.iterator();
            while (iter.hasNext()) {
                MonkeyTradeData d = iter.next();
                if (d.getMonkeyId() == monkeyId) {
                    data = d;
                    break;
                }
            }
        }
        if (null == data) {
            return new MonkeyMarryData(ErrorCode.GET_DATA_ERROR);
        } else if (user.getMoney().floatValue() < data.getPrice()) {
            return new MonkeyMarryData(ErrorCode.WALLET_LESS_ERROR);
        } else if (!price.equals(data.getPrice())) {
            return new MonkeyMarryData(ErrorCode.ORDER_PRICE_ERROR);
        }

        Monkey dadMonkey = monkeyService.getMonkey(monkeyId);
        Monkey momMonkey = monkeyService.getMonkey(myMonkeyId);
        if (null == dadMonkey || null == momMonkey) {
            return new MonkeyMarryData(ErrorCode.GET_DATA_ERROR);
        }
        if (dadMonkey.getStatus() != MONKEY_STATUS_MARRY) {
            return new MonkeyMarryData(ErrorCode.MONKEY_STATUS_ERROR);
        }
        if (dadMonkey.getUserId() == userId) {
            return new MonkeyMarryData(ErrorCode.YOUR_MONKEY_ERROR);
        }
        if (momMonkey.getStatus() != MONKEY_STATUS_NORMAL) {
            return new MonkeyMarryData(ErrorCode.MONKEY_STATUS_ERROR);
        }
        if (momMonkey.getUserId() != userId) {
            return new MonkeyMarryData(ErrorCode.NO_OWNER_MONKEY);
        }

        // 判断生育时间
        if (dadMonkey.getReproductCD().after(new Date())) {
            return new MonkeyMarryData(ErrorCode.REPRODUCT_CD_ERROR);
        }
        if (momMonkey.getReproductCD().after(new Date())) {
            return new MonkeyMarryData(ErrorCode.REPRODUCT_CD_ERROR);
        }

        data = null;
        synchronized (this) {
            Iterator<MonkeyTradeData> iter = sellList.iterator();
            while (iter.hasNext()) {
                MonkeyTradeData d = iter.next();
                if (d.getMonkeyId() == monkeyId) {
                    dadMonkey.setStatus(MONKEY_STATUS_NORMAL);
                    iter.remove();
                    data = d;
                    break;
                }
            }
        }
        if (null == data) {
            return new MonkeyMarryData(ErrorCode.GET_DATA_ERROR);
        }

        // 扣钱
        if (ErrorCode.SUCCESS != userService.updateUserMoney(user, monkeyId, -price, 0,
                MONKEY_STATUS_NORMAL, ACTION_MARRY)) {
            return new MonkeyMarryData(ErrorCode.WRITE_DB_ERROR);
        }

        // 转钱，扣手续费
        float water = webConfig.getFloat("marry.pay.water");
        User user1 = userService.getUser(data.getUserId());
        if (ErrorCode.SUCCESS != userService.updateUserMoney(user1, monkeyId, price * (1 - water),
                price * water, MONKEY_STATUS_MARRY, ACTION_MARRY)) {
            return new MonkeyMarryData(ErrorCode.WRITE_DB_ERROR);
        }

        // 生育时间 = 标准生育时间 *（1+生育系数）^（已生育次数）* 代系数 / (1+生育值^2/10)
        Date now = new Date();
        int g1 = dadMonkey.getGeneration();
        int g2 = momMonkey.getGeneration();
        int rc1 = dadMonkey.getReproductCount() + 1;
        int rc2 = momMonkey.getReproductCount() + 1;
        float r1 = dadMonkey.getReproduct() * 1.0f / 100;
        float r2 = momMonkey.getReproduct() * 1.0f / 100;
        int base = webConfig.getInt("monkey.reproduct.base");
        float param = 1 + webConfig.getFloat("monkey.reproduct.param");
        int dadCD = (int) (base * Math.pow(param, rc1) * countUtil.generation(g1) / (1 + Math.pow(r1, 3) / 10));
        int momCD = (int) (base * Math.pow(param, rc2) * countUtil.generation(g2) / (1 + Math.pow(r2, 3) / 10));
        logger.info("marry dad:{}, g:{}, c:{}, r:{}, {} * {} * {} / ( 1 + {} / 10) = {}", monkeyId, g1, rc1, r1, base,
                Math.pow(param, rc1), countUtil.generation(g1), Math.pow(r1, 3), dadCD);
        logger.info("marry mom:{}, g:{}, c:{}, r:{}, {} * {} * {} / ( 1 + {} / 10) = {}", myMonkeyId, g2, rc2, r2, base,
                Math.pow(param, rc2), countUtil.generation(g2), Math.pow(r2, 3), momCD);

        dadMonkey.setReproductCount(rc1);
        momMonkey.setReproductCount(rc2);

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        calendar.add(Calendar.MINUTE, dadCD);
        dadMonkey.setReproductCD(calendar.getTime());

        // 写入数据库
        TMonkey tMonkey = new TMonkey();
        tMonkey.setReproductcount(rc1);
        tMonkey.setReproductcd(dadMonkey.getReproductCD());
        TMonkeyExample example = new TMonkeyExample();
        example.or().andMonkeyidEqualTo(monkeyId);
        example.setLimit(1);
        if (0 == monkeyMapper.updateByExampleSelective(tMonkey, example)) {
            logger.info("marry update dad reproductcd error:{}, {}, {}", userId, monkeyId, myMonkeyId);
        }

        calendar.setTime(now);
        calendar.add(Calendar.MINUTE, momCD);
        momMonkey.setReproductCD(calendar.getTime());

        // 写入数据库
        tMonkey = new TMonkey();
        tMonkey.setReproductcount(rc2);
        tMonkey.setReproductcd(momMonkey.getReproductCD());
        example = new TMonkeyExample();
        example.or().andMonkeyidEqualTo(myMonkeyId);
        example.setLimit(1);
        if (0 == monkeyMapper.updateByExampleSelective(tMonkey, example)) {
            logger.info("marry update dad reproductcd error:{}, {}, {}", userId, monkeyId, myMonkeyId);
        }

        // 生成新宠物，生成hash
        int g = (g1 > g2) ? g1 + 1 : g2 + 1; // 最差代数+1
        MonkeyCreate create = monkeyService.createData();

        // 获取父亲套装
        String[] strs = dadMonkey.getUuid().split("-");
        Monkey monkey = monkeyService.create(g, monkeyId, myMonkeyId, user, MONKEY_FROM_MARRY, Integer.valueOf(strs[0]), create, null);
        if (null == monkey) {
            return new MonkeyMarryData(ErrorCode.CREATE_MONKEY_ERROR);
        }

        // 更新头像
        userService.updateUserHead(user, monkey);

        //
        TMonkeyReproduct reproduct = new TMonkeyReproduct();
        reproduct.setDadid(monkeyId);
        reproduct.setMomid(myMonkeyId);
        reproduct.setSonid(monkey.getId());
        reproduct.setUuid(create.getUuid());
        reproduct.setCreatetime(now);
        if (0 == monkeyReproductMapper.insert(reproduct)) {
            logger.info("marry insert monkey reproduct:{}, {}", userId, monkey.getId());
        }

        // 更新族谱
        SimpleDateFormat sdf = dateUtil.getDateFormat();
        MonkeyReproduct monkeyReproduct = new MonkeyReproduct(reproduct, dadMonkey.getUuid(), momMonkey.getUuid(), monkey.getUuid(), sdf);
        List<MonkeyReproduct> dadMonkeyFamilies = dadMonkey.getFamilies();
        dadMonkeyFamilies.add(monkeyReproduct);
        List<MonkeyReproduct> momMonkeyFamilies = momMonkey.getFamilies();
        momMonkeyFamilies.add(monkeyReproduct);

        TMonkeyReproduct reproduct2 = new TMonkeyReproduct();
        reproduct2.setDadid(monkeyId);
        reproduct2.setMomid(myMonkeyId);
        reproduct2.setSonid(monkey.getId());
        reproduct2.setUuid(create.getUuid());
        reproduct2.setCreatetime(now);
        if (0 == monkeyReproductMapper.insert(reproduct2)) {
            logger.info("marry service insert reproduct error:{}, {}, {}",
                    monkeyId, myMonkeyId, monkey.getId());
        }

        // 写入记录
        TMarryOrder order = new TMarryOrder();
        order.setTouserid(userId);
        order.setTomonkey(myMonkeyId);
        order.setStatus(ORDER_COMPLETE);
        TMarryOrderExample example1 = new TMarryOrderExample();
        example1.or().andUseridEqualTo(dadMonkey.getUserId()).andMonkeyidEqualTo(monkeyId).andStatusEqualTo(ORDER_CREATE);
        example1.setOrderByClause("createTime DESC");
        example1.setLimit(1);
        if (0 == marryOrderMapper.updateByExampleSelective(order, example1)) {
            logger.info("marry order update complete db error:{}, {}", userId, monkeyId);
        }

        template.delete(REDIS_PRE + monkeyId);
        logger.info("marry:{}, {}, p:{}, to:{}, {}, n:{}", userId, myMonkeyId, price, user1.getId(), monkeyId, monkey.getId());
        return new MonkeyMarryData(ErrorCode.SUCCESS, monkey.getId());
    }
}
